#pragma once

#include "Graph.h"
#include <set>
#include <stdexcept>
#include <map>


namespace{

	class WeighEdge : public Edge{
		int weight_;
	public:
		WeighEdge(int fromVertex_, int toVertex_, int weight){
			fromVertex = fromVertex_;
			toVertex = toVertex_;
			weight_ = weight;
		}
		const Edge getUnweighedEdge() const{
			return{ fromVertex, toVertex };
		}
		const int getWeight() const{
			return weight_;
		}
	};

	struct WeightAdjacent{
		int weight;
		int toVertex;
		WeightAdjacent(int weight, int toVertex) : weight(weight), toVertex(toVertex) {}
		WeightAdjacent(){}
	};

	bool operator < (const WeighEdge& firstEdge, const WeighEdge& secondEdge){
		if (firstEdge.getWeight() < secondEdge.getWeight())
			return true;
		if (firstEdge.getWeight() > secondEdge.getWeight())
			return false;
		return (firstEdge.getUnweighedEdge() < secondEdge.getUnweighedEdge());
	}

	class WeighGraph{
		std::map<Edge, int> edgesWeight_;
		std::unique_ptr<Graph> unWeighedGraph_;
	public:
		WeighGraph(int vertexNum, const std::vector< WeighEdge>& edgesList){
			std::vector<Edge> unWeighedEdgesList;
			for (auto currentEdge : edgesList){
				unWeighedEdgesList.push_back(currentEdge.getUnweighedEdge());
			}
			std::sort(unWeighedEdgesList.begin(), unWeighedEdgesList.end());
			auto iterator = std::unique(unWeighedEdgesList.begin(), unWeighedEdgesList.end());
			unWeighedEdgesList.resize(std::distance(unWeighedEdgesList.begin(), iterator));
			unWeighedGraph_ = readGraph(vertexNum, unWeighedEdgesList);
			for (size_t currentEdgeNum = 0; currentEdgeNum < edgesList.size(); ++currentEdgeNum){
				if (edgesWeight_.count(edgesList[currentEdgeNum].getUnweighedEdge()) == 0){
					edgesWeight_[edgesList[currentEdgeNum].getUnweighedEdge()] = edgesList[currentEdgeNum].getWeight();
				}
				else{
					int currentWeight = edgesWeight_[edgesList[currentEdgeNum].getUnweighedEdge()];
					edgesWeight_[edgesList[currentEdgeNum].getUnweighedEdge()] = std::min<int>(currentWeight, edgesList[currentEdgeNum].getWeight());
				}
			}
		}

		const int getVertexNum() const{
			return unWeighedGraph_->getVertexNum();
		}
		const std::vector<WeightAdjacent> getAdjacentWeights(int vertex) const{
			std::vector<int> adjacentList = unWeighedGraph_->getAdjacentList(vertex);
			std::vector<WeightAdjacent> weightsList;
			for (auto currentVertex : adjacentList){
				weightsList.push_back(WeightAdjacent(edgesWeight_.find(Edge(vertex, currentVertex))->second, currentVertex));
			}
			return weightsList;
		}
		const int getEdgeWeight(const Edge& edge) const {
			if (edgesWeight_.count(edge) == 0){
				throw std::runtime_error("There isn't edge that you are requested");
			}
			else
				return edgesWeight_.find(edge)->second;
		}
	};
}
